#pragma once
#include "common.hpp"
#include <memory>
#include "Socket.hpp"

#include <sys/time.h>
#include <sys/types.h>
#include <poll.h>
#include <unistd.h>


#define DEFAULT_BACKLOG 16 //最大同时listen连接数
#define DEFAULT_FD -1 //无效文件描述符
#define DEFAULT_FDS_SIZE 4096 //_fds初始长度

class poll_server
{
private:
    void print_fds()
    {
        std::cout << "_fds: ";
        for(int i = 0; i < _nfds; ++i)
        {
            if(_fds[i].fd != DEFAULT_FD)
                std::cout << _fds[i].fd << " ";
        }
        std::cout << std::endl;//换行 + 刷新缓冲区
    }


public:
    poll_server(uint16_t port)
        :_listen_socket_ptr(std::make_unique<TCP_Socket>())
        ,_port(port)
        ,_running_tag(false)
        ,_nfds(DEFAULT_FDS_SIZE)
    {
        _fds = new struct pollfd[_nfds];
        for(int i = 0; i < _nfds; ++i)
        {
            _fds[i].fd = DEFAULT_FD;
            _fds[i].events = _fds[i].revents = 0;
        }

        _listen_socket_ptr->create_listen_socket(_port, DEFAULT_BACKLOG);
        _fds[0].fd = _listen_socket_ptr->get_socket_fd();//辅助数组的第一位(0)fd 设为监听socket
        _fds[0].events = POLLIN; //设置监视listen_socket_fd上的读事件
    }

    ~poll_server()
    {
        delete[] _fds;
    }



    void start()
    {
        LOG(wzx::log_level::DEBUG) << "select server start...";
        _running_tag = true;
        while(_running_tag)
        {
            //debug
            print_fds();

            //因为poll函数不改变events, 所以不用每次进循环都更新参数了

            int ready_fd_count = poll(_fds, _nfds, -1/*阻塞监视*/);

            if(ready_fd_count > 0) //有若干个fd的事件就绪了
            {
                LOG(wzx::log_level::INFO) << "several events are ready!!!  ready_fd_count: " << ready_fd_count;

                //交给事件派发器
                event_dispatcher();
                continue;
            }
            else if(ready_fd_count == 0) //超时了 -- 没有事件就绪
            {
                LOG(wzx::log_level::INFO) << "time out!!!";
                continue;
            }
            else //ready_fd_count < 0 -- poll()出错了
            {
                LOG(wzx::log_level::FATAL) << "poll error: " << errno;
                break;
            }

        }

        _running_tag = false;
    }


    //事件派发器 -- 我们这里只设计了读事件
    void event_dispatcher()
    {
        //两种读事件就绪: 1.新连接请求;  2.IO
        for(int i = 0; i < _nfds; ++i)
        {
            if(_fds[i].fd == DEFAULT_FD)//无效fd
                continue;

            if(_fds[i].revents & POLLIN) //该fd上的[读事件]已就绪
            {
                //1. 新连接请求
                if(_fds[i].fd == _listen_socket_ptr->get_socket_fd())
                    connection_processer();
                
                //2. IO
                else
                    IO_processer(_fds[i].fd, i);

            }
        }

    }


    //连接处理器 -- accept
    void connection_processer()
    {
        //此时accept一定不会阻塞, 因为_listen_socket_fd上已经有事件就绪了
        Inet_Addr client;
        int socket_fd = _listen_socket_ptr->accept_socket(client);

        //不能直接用socket_fd进行IO, 否则又会变成阻塞IO了,
        //这里要将它托管给poll -- 加入_fds
        for(int i = 0; i < _nfds; ++i)
        {
            if(_fds[i].fd == DEFAULT_FD)//有空位
            {
                _fds[i].fd = socket_fd;
                _fds[i].events |= POLLIN;//让poll监视该fd上的读事件
                LOG(wzx::log_level::DEBUG) << "socket_fd insert into _fd_array successful!!!  socket_fd: " << socket_fd;
                return;
            }
        }
        //辅助数组里面已经没有空位了 -- poll这里可以扩容
        LOG(wzx::log_level::DEBUG) << "select server is full, expansion in progress...";
        // realloc(_fds, _nfds * 2); -- 扩容大概逻辑如此, 实际更复杂一点
        // _nfds *= 2;
    }


    //IO处理器
    void IO_processer(int socket_fd, int pos/*该fd在_fds中的位置*/)
    {
        //此时recv一定不会阻塞, 因为socket_fd上已经有事件就绪了
        std::unique_ptr<Socket> socket_ptr = std::make_unique<TCP_Socket>(socket_fd);
        std::string message;//bug -- 这里读到的不一定是完整的 -- 但是我这里就简略一下 不进行完整报文处理了(>_<)
        bool ret_recv = socket_ptr->receive(message);
        if(ret_recv == true)
        {
            std::cout << "client say:> " << message << std::endl;
        }
        else //ret_recv == false; -- 对端关闭连接了
        {
            //1. 不要让poll再监视该fd
            _fds[pos].fd = DEFAULT_FD;
            _fds[pos].events = _fds[pos].revents = 0;

            //2. 关闭fd
            socket_ptr->close();
        }
    }



private:
    std::unique_ptr<Socket> _listen_socket_ptr;
    uint16_t _port;

    bool _running_tag;

    struct pollfd* _fds;//因为输入输出分离了, 所以就不用每次循环都更新参数了, 进而就不需要维护辅助数组了
    nfds_t _nfds;//_fds数组的长度
};